<?php

namespace App\Lib\Pay\Wxpay;

use App\Lib\Pay\Wxpay\WxPayData;
use App\Lib\Pay\Wxpay\WxPayConfig;
use App\Lib\Pay\Wxpay\WxJsPayConfig;
class WxPayApi
{
    
    public function __construct()
    {
    }
    
    /**
     * app 统一下单
     */
    public static function unifiedOrder($inputObj, $pay_type = 0)
    {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    
        //检测必填参数
        if(!$inputObj->IsOut_trade_noSet()) {
            throw new \Exception("缺少统一支付接口必填参数out_trade_no！");
        }else if(!$inputObj->IsBodySet()){
            throw new \Exception("缺少统一支付接口必填参数body！");
        }else if(!$inputObj->IsTotal_feeSet()) {
            throw new \Exception("缺少统一支付接口必填参数total_fee！");
        }else if(!$inputObj->IsTrade_typeSet()) {
            throw new \Exception("缺少统一支付接口必填参数trade_type！");
        }
    
        //关联参数
        if($inputObj->GetTrade_type() == "JSAPI" && !$inputObj->IsOpenidSet()){
            throw new \Exception("统一支付接口中，缺少必填参数openid！trade_type为JSAPI时，openid为必填参数！");
        }
        if($inputObj->GetTrade_type() == "NATIVE" && !$inputObj->IsProduct_idSet()){
            throw new \Exception("统一支付接口中，缺少必填参数product_id！trade_type为JSAPI时，product_id为必填参数！");
        }
        
        $config = new WxPayConfig();
        if ($pay_type == 1){
            $config = new WxJsPayConfig();
        }
        
        $inputObj->SetAppid($config->GetAppId());
        $inputObj->SetMch_id($config->GetMerchantId());
        $inputObj->SetSpbill_create_ip($inputObj->get_client_ip());
        $inputObj->SetNonce_str($inputObj->getNonceStr());
        
        if(!$inputObj->IsNotify_urlSet() && $config->GetNotifyUrl() != ""){
            $inputObj->SetNotify_url($config->GetNotifyUrl());//异步通知url
        }
       
        $inputObj->SetSign($config);
        $xml = $inputObj->ToXml();
        
        $response = self::postXmlCurl($config, $xml, $url, false, 30);
        $ret = self::xmlToArray($response);
        //通信失败
        if(!array_key_exists("return_code", $ret) ||(array_key_exists("return_code", $ret) && $ret['return_code'] != "SUCCESS")) {
            $return['flag'] = false;
            $return['msg'] = $ret['return_msg'];
            return $return;
        }
        //预订单创建失败
        if(!array_key_exists("result_code", $ret) || (array_key_exists("result_code", $ret) && $ret['result_code'] != "SUCCESS")) {
            $return['flag'] = false;
            $return['msg'] = $ret['err_code_des'];
            return $return;
        }
        //成功
        $return['flag'] = true;
        $return['msg'] = 'ok';
        $return['data'] = [
            'appid' => $ret['appid'],
            'partnerid' => $ret['mch_id'],
            'prepayid' => $ret['prepay_id'],
            'package' => 'Sign=WXPay',
            'noncestr' => $inputObj->getNonceStr(),
            'timestamp' => (string)time()
        ];
        $sign = self::MakeSign($return['data'], $config);
        $return['data']['sign'] = $sign;
        return $return;
    }
    
    /**
     * 支付回调验签
     * */
    public static function checkSign($param)
    {
        $config = new WxPayConfig();
        $sign = $param['sign'];
        if(self::MakeSign($param,$config) != $sign){
            return false;
        }
        return true;
    }
    
    /**
     * 输出给客户端的签名
     * */
    public static function MakeSign($param, $config)
    {
        
        ksort($param);
        
        $string = self::ToUrlParams($param);
        
        $string = $string . "&key=".$config->GetKey();
       
        if($config->GetSignType() == "MD5"){
            $string = md5($string);
        } else if($config->GetSignType() == "HMAC-SHA256") {
            $string = hash_hmac("sha256",$string ,$config->GetKey());
        } else {
            throw new \Exception("签名类型不支持！");
        }
        
        $result = strtoupper($string);
        return $result;
    }
    
    /**
     * 格式化参数格式化成url参数
     */
    public static function ToUrlParams($param)
    {
        $buff = "";
        foreach ($param as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        
        $buff = trim($buff, "&");
        return $buff;
    }
    
    
    /**
     * 以post方式提交xml到对应的接口url
     *
     * @param WxPayConfigInterface $config  配置对象
     * @param string $xml  需要post的xml数据
     * @param string $url  url
     * @param bool $useCert 是否需要证书，默认不需要
     * @param int $second   url执行超时时间，默认30s
     * @throws WxPayException
     */
    private static function postXmlCurl($config, $xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        $curlVersion = curl_version();
        
        $ua = "WXPaySDK/3.0.9 (".PHP_OS.") PHP/".PHP_VERSION." CURL/".$curlVersion['version']." "
            .$config->GetMerchantId();
        
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        
        $proxyHost = "0.0.0.0";
        $proxyPort = 0;
        $config->GetProxy($proxyHost, $proxyPort);
        //如果有配置代理这里就设置代理
        if($proxyHost != "0.0.0.0" && $proxyPort != 0){
            curl_setopt($ch,CURLOPT_PROXY, $proxyHost);
            curl_setopt($ch,CURLOPT_PROXYPORT, $proxyPort);
        }
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);//严格校验
        curl_setopt($ch,CURLOPT_USERAGENT, $ua);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        
        if($useCert == true){
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            //证书文件请放入服务器的非web目录下
            $sslCertPath = "";
            $sslKeyPath = "";
            $config->GetSSLCertPath($sslCertPath, $sslKeyPath);
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT, $sslCertPath);
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, $sslKeyPath);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new \Exception("curl出错，错误码:$error");
        }
    }
    
    /**
     * 输出处理成功
     * */
    public static function outSuccess()
    {
        return
            "<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
            </xml>";
        
    }
    
    /**
     *  作用：将xml转为array
     */
    public static function xmlToArray($xml){
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }
    
}